﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace NearForums.DataAccess.Xml
{
    class UsersDataAccess : BaseDataAccess, IUsersDataAccess
    {

        public UsersDataAccess()
        {

        }

        public void AddEmail(int id, string email, EmailPolicy policy)
        {
            throw new NotImplementedException();
        }

        public User AddUser(User user, AuthenticationProvider provider, string providerId)
        {
            // nullable fields
            // user group id
            // user provider id
            string filePath = this.GetDataPath(UsersFile);
            XDocument doc = XDocument.Load(filePath);
            int count = int.Parse(doc.Root.Attribute("Count").Value);

            user.Id = ++count;
            user.Guid = Guid.NewGuid();
            if (count == 1)
            {
                user.Role = UserRole.Admin;
            }
            user.RoleName = GetRoleName(user.Role);
            XElement elem = new XElement("User",
                                new XElement("UserId", user.Id),
                                new XElement("UserName", user.UserName),
                                new XElement("GroupId", user.Role),
                                new XElement("Active", true),
                                new XElement("Profile", user.Profile),
                                new XElement("Signature", user.Signature),
                                new XElement("BirthDate", ToString(user.BirthDate)),
                                new XElement("Website", user.Website),
                                new XElement("Guid", user.Guid),
                                new XElement("Timezone", user.TimeZone.ToString()),
                                new XElement("Email", user.Email),
                                new XElement("EmailPolicy", user.EmailPolicy),
                                new XElement("Photo", user.Photo),
                                new XElement("RegistrationDate", ToString(DateTime.Now)),
                                new XElement("ExternalProfileUrl", user.ExternalProfileUrl),
                                new XElement("Provider", provider.ToString()),
                                new XElement("ProviderId", providerId),
                                new XElement("ProviderLastCall", ToString(user.ProviderLastCall)),
                                new XElement("PasswordResetGuid", user.PasswordResetGuid),
                                new XElement("PasswordResetGuidExpireDate", ToString(user.PasswordResetGuidExpireDate)),
                                new XElement("Subscriptions")
                            );

            doc.Root.Add(elem);
            doc.Root.SetAttributeValue("Count", count);

            doc.Save(filePath);

            return user;
        }

        public bool Ban(int id, int moderatorId, ModeratorReason reason, string reasonText)
        {
            throw new NotImplementedException();
        }

        public void Delete(int id)
        {
            string filePath = GetDataPath(UsersFile);
            XDocument doc = XDocument.Load(filePath);
            var qry = from u in doc.Root.Elements("User")
                      where (int)u.Element("UserId") == id
                      select u;
            var elem = qry.SingleOrDefault();
            if (elem != null)
            {
                elem.SetElementValue("Active", false);
                doc.Save(filePath);
            }
        }

        public void Demote(int id)
        {
            string filePath = GetDataPath(UsersFile);
            XDocument doc = XDocument.Load(filePath);
            var qry = from u in doc.Root.Elements("User")
                      where (int)u.Element("UserId") == id
                      select u;
            var elem = qry.SingleOrDefault();
            if (elem != null)
            {
                int userGroupId = (int)(UserRole)(Enum.Parse(typeof(UserRole), (string)elem.Element("GroupId")));
                var roles = GetRoles();
                var groupIds = roles.Select(kvp => (int)kvp.Key).OrderByDescending(i => i);
                bool found = false;
                foreach (var groupId in groupIds)
                {
                    if (groupId < userGroupId)
                    {
                        userGroupId = groupId;
                        found = true;
                        break;
                    }
                }

                if (found)
                {
                    elem.SetElementValue("GroupId", ((UserRole)userGroupId).ToString());
                    doc.Save(filePath);
                }
            }
        }

        public void Edit(User user)
        {
            string filePath = this.GetDataPath(UsersFile);
            XDocument doc = XDocument.Load(filePath);

            var qry = from u in doc.Root.Elements("User")
                      where (int)u.Element("UserId") == user.Id
                      select u;

            var elem = qry.SingleOrDefault();

            if (elem != null)
            {
                elem.SetElementValue("Profile", user.Profile);
                elem.SetElementValue("Signature", user.Signature);
                if (user.BirthDate.HasValue)
                    elem.SetElementValue("BirthDate", ToString(user.BirthDate.Value));
                elem.SetElementValue("Website", user.Website);
                elem.SetElementValue("Timezone", user.TimeZone.ToString());
                elem.SetElementValue("Email", user.Email);
                elem.SetElementValue("EmailPolicy", user.EmailPolicy);
                elem.SetElementValue("Photo", user.Photo);

                doc.Save(filePath);
            }

        }

        public User Get(int userId)
        {
            string filePath = this.GetDataPath(UsersFile);
            XDocument doc = XDocument.Load(filePath);
            var qry = from u in doc.Root.Elements("User")
                      where (int)u.Element("UserId") == userId
                      select u;
            XElement elem = qry.SingleOrDefault();
            User user = null;
            if (elem != null)
            {
                user = GetUserFromElement(elem);
            }
            return user;
        }

        public List<User> GetAll()
        {
            // Get all users
            XDocument doc = XDocument.Load(GetDataPath(UsersFile));
            var qry = from u in doc.Root.Elements("User")
                      where (bool)u.Element("Active") == true
                      select u;
            List<User> users = new List<User>();
            foreach (var elem in qry)
            {
                users.Add(GetUserFromElement(elem));
            }
            return users;
        }

        public List<User> GetByName(string userName)
        {
            throw new NotImplementedException();
        }

        public User GetByPasswordResetGuid(AuthenticationProvider provider, string PasswordResetGuid)
        {
            var doc = XDocument.Load(GetDataPath(UsersFile));
            var qry = from u in doc.Root.Elements("User")
                      where (string)u.Element("PasswordResetGuid") == PasswordResetGuid
                      select u;
            var elem = qry.SingleOrDefault();
            if (elem != null)
            {
                return GetUserFromElement(elem);
            }
            return null;
        }

        public User GetByProviderId(AuthenticationProvider provider, string providerId)
        {
            string filePath = this.GetDataPath(UsersFile);
            XDocument doc = XDocument.Load(filePath);
            var qry = from u in doc.Root.Elements("User")
                      where u.Element("Provider").Value == provider.ToString()
                      && u.Element("ProviderId").Value == providerId
                      select u;
            XElement elem = qry.SingleOrDefault();
            User user = null;
            if (elem != null)
            {
                user = GetUserFromElement(elem);
            }
            return user;
        }

        public string GetRoleName(UserRole userRole)
        {
            return GetRoles()[userRole];
        }

        public Dictionary<UserRole, string> GetRoles()
        {
            string filePath = GetDataPath(GroupsFile);
            XDocument doc = XDocument.Load(filePath);
            var qry = from g in doc.Root.Elements("Group")
                      select g;

            return qry.ToDictionary(g => (UserRole)Enum.Parse(typeof(UserRole), g.Attribute("Id").Value),
                                    g => g.Attribute("Name").Value);
        }

        public User GetTestUser()
        {
            throw new NotImplementedException();
        }

        public void Promote(int id)
        {
            string filePath = GetDataPath(UsersFile);
            XDocument doc = XDocument.Load(filePath);
            var qry = from u in doc.Root.Elements("User")
                      where (int)u.Element("UserId") == id
                      select u;
            var elem = qry.SingleOrDefault();
            if (elem != null)
            {
                int userGroupId = (int)(UserRole)(Enum.Parse(typeof(UserRole), (string)elem.Element("GroupId")));
                var roles = GetRoles();
                var groupIds = roles.Select(kvp => (int)kvp.Key).OrderBy(i => i);
                bool found = false;
                foreach (var groupId in groupIds)
                {
                    if (groupId > userGroupId)
                    {
                        userGroupId = groupId;
                        found = true;
                        break;
                    }
                }

                if (found)
                {
                    elem.SetElementValue("GroupId", ((UserRole)userGroupId).ToString());
                    doc.Save(filePath);
                }
            }
        }

        public bool Suspend(int id, int moderatorId, ModeratorReason reason, string reasonText, DateTime endDate)
        {
            throw new NotImplementedException();
        }

        public void UpdatePasswordResetGuid(int id, string Guid, DateTime expireDate)
        {
            string filePath = GetDataPath(UsersFile);
            XDocument doc = XDocument.Load(filePath);
            var qry = from u in doc.Root.Elements("User")
                      where (int)u.Element("UserId") == id
                      select u;
            var elem = qry.SingleOrDefault();
            if (elem != null)
            {
                elem.SetElementValue("PasswordResetGuid", Guid);
                elem.SetElementValue("PasswordResetGuidExpireDate", ToString(expireDate));
                doc.Save(filePath);
            }
        }

        public bool Warn(int id, int moderatorId, ModeratorReason reason, string reasonText)
        {
            throw new NotImplementedException();
        }

        private User GetUserFromElement(XElement elem)
        {
            var user = new User();
            user.Id = (int)elem.Element("UserId");
            user.UserName = (string)elem.Element("UserName");
            user.Role = (UserRole)Enum.Parse(typeof(UserRole), (string)elem.Element("GroupId"));
            user.RoleName = GetRoleName(user.Role);
            user.Profile = (string)elem.Element("Profile");
            user.Signature = (string)elem.Element("Signature");
            user.BirthDate = ToNullableTime((string)elem.Element("BirthDate"));
            user.Website = (string)elem.Element("Website");
            user.Guid = (Guid)elem.Element("Guid");
            user.TimeZone = TimeSpan.Parse((string)elem.Element("Timezone"));
            user.Email = (string)elem.Element("Email");
            user.EmailPolicy = (EmailPolicy)Enum.Parse(typeof(EmailPolicy), (string)elem.Element("EmailPolicy"));
            user.Photo = (string)elem.Element("Photo");
            user.RegistrationDate = ToTime((string)elem.Element("RegistrationDate"));
            user.ExternalProfileUrl = (string)elem.Element("ExternalProfileUrl");
            user.ProviderLastCall = ToTime((string)elem.Element("ProviderLastCall"));
            user.PasswordResetGuid = (string)elem.Element("PasswordResetGuid");
            user.PasswordResetGuidExpireDate = ToTime((string)elem.Element("PasswordResetGuidExpireDate"));
            return user;
        }
    }
}
